import copy
import json
from distutils.util import strtobool
from sqlalchemy.orm import Session
from app.monitor_metrics.models.prom_host_info import PromHostInfo
from app.monitor_metrics.models.prom_host_group import PromHostGroup
from app.monitor_metrics.models.prom_alarm_template import PromAlarmTemplate
from app.monitor_metrics.models.prom_alarm_strategy import PromAlarmStrategy
from app.monitor_metrics.models.prom_alarm_set import PromAlarmSet
from app.monitor_metrics.models.prom_alarm_group import PromAlarmGroup
from app.monitor_metrics.models.prom_alarm_rule import PromAlarmRule
from app.monitor_metrics.handlers.prom_alarm_template_handler import PromAlarmTemplateHandler
from app.monitor_metrics.handlers.prom_alarm_set_handler import PromAlarmSetHandler
from app.monitor_metrics.handlers.prom_alarm_group_handler import PromAlarmGroupHandler
from app.monitor_metrics.handlers.prom_alarm_strategy_handler import PromAlarmStrategyHandler
from app.audit.handlers.audit_event_handler import AuditEventHandler
from app.monitor_metrics.utils import PromCommonUtils
from common.consul_client import ConsulClient
from core.response import resp_400, resp_500, HTTPExceptionCustom
from core.logger import logger


class PromHostInfoHandler():
    """  host info Handler
    """
    def __init__(self):
        self.prom_alarm_template_handler = PromAlarmTemplateHandler()
        self.prom_alarm_strategy_handler = PromAlarmStrategyHandler()
        self.prom_alarm_set_handler = PromAlarmSetHandler()
        self.prom_alarm_group_handler = PromAlarmGroupHandler()
        self.consul_client = ConsulClient()


    def get_host_info_search(self, db: Session, page: int, per_page: int, field: str, value: str):
        """
        根据表字段模糊匹配，获取主机信息
        """
        try:
            field_info = {field: value}
            order_info = {"name": "asc"}
            result = PromHostInfo.filter_by_field_search(db, page, per_page, field_info, order_info)
            if not result:
                logger.error(f"get host info, result is null => {field}:{value}")
                return False, None

            # 过滤内部标签
            for item in result["items"]:
                item["labels"] = {
                    key: value
                    for key, value in item["labels"].items()
                    if not key.startswith("i_")
                }

        except Exception as e:
            message = f"get host info by {field}:{value} error "
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return True, result



    def get_host_info_match(self, db: Session, page: int, per_page: int, field: str, value: str):
        """
        根据表字段完全匹配，获取主机信息
        """
        try:
            result = PromHostInfo.filter_by_field_match(db, page, per_page, {field: value})
            if not result:
                logger.error(f"get host info match, result is null => {field}:{value}")
                return False, None

            # 过滤内部标签
            for item in result["items"]:
                item["labels"] = {
                    key: value
                    for key, value in item["labels"].items()
                    if not key.startswith("i_")
                }
        except Exception as e:
            message = f"get host info match by {field}:{value} error"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return True, result


    def add_host_to_consul(self, db: Session, data):
        # 主机没有使能，直接返回
        if not strtobool(str(data["state"])):
            logger.info(f"host state:{data['state']}, not to consul")
            return True

        # 添加consul服务
        result = self.consul_client.create_consul_service(data)
        if not result:
            logger.error(f"add host to consul failed {data['ip']}:{data['port']}")
            return False

        return True



    def add_host_to_database(self, db: Session, data):
        try:
            host_object = PromHostInfo.get_object_by_name(db, data["name"])
            if host_object:
                # 如果存在， 则异常返回
                logger.error(f"add host failed, {data['name']} was exist")
                return False

            group_object = PromHostGroup.get_object_by_uuid(db, data["host_group_uuid"])
            if not group_object:
                # 如果指定组不存在，则异常返回
                logger.error(f"add host failed, group:{data['group']} not exist")
                return False

            # 添加数据库
            host_group_info = PromHostGroup.get_object_info_by_uuid(db, data["host_group_uuid"])
            data["labels"]["i_department"] = host_group_info["department"]
            data["labels"]["i_host_group"] = host_group_info["name"]
            data["labels"]["i_host_name"] = data["name"]

            result = PromHostInfo.add(db, data)
            if not result:
                logger.error(f"add host info to database failed")
                return False
        except Exception as e:
            message = f"add host to database error"
            logger.exception(f"{message} => {e}")
            return False

        return True

    def host_add_alarm_template(self, db: Session, data: dict):
        # 创建主机自定义的告警模板
        alarm_template_data = {
            "name": f"host自定义告警模板:{data['name']}",
            "type": "auto",
            "classes": "ECS服务器",
            "comment": f"此模板自动创建，只用于host:{data['name']}自定义的告警策略"
        }
        result = self.prom_alarm_template_handler.add_alarm_template(db, alarm_template_data)
        if not result:
            logger.error(f"add alarm template to database failed")
            return False

        alarm_template_info = PromAlarmTemplate.get_object_info_by_name(db, alarm_template_data["name"])
        data["template_uuid"] = alarm_template_info['uuid']


        return True


    def host_add_alarm_set(self, db: Session, host_info: dict):
        """
        增加主机的规则集
        """
        alarm_set_name = PromCommonUtils.get_alarm_set_name(host_info)
        if PromAlarmSet.get_object_by_name(db, alarm_set_name):
            logger.warning(f"add_alarm_set: alarm_set_name:{alarm_set_name} was exist")
            return True

        # 创建主机的规则集.
        alarm_set_data = {
            "name": alarm_set_name,
            "classes": "ECS服务器",
            "comment": f"{host_info['name']}:{host_info['ip']} 所有的告警规则"
        }
        result = self.prom_alarm_set_handler.add_alarm_set(db, alarm_set_data)
        if not result:
            logger.error(f"add_alarm_set: add alarm set failed")
            return False

        return True


    def host_add_alarm_rule(self, db: Session, host_info: dict):
        """
        创建告警规则
        """
        # 增加主机的规则集
        result = self.host_add_alarm_set(db, host_info)
        if not result:
            logger.error(f"host_add_alarm_rule: add alarm set failed")
            return False

        # 创建主机的自定义规则组
        alarm_set_name = PromCommonUtils.get_alarm_set_name(host_info)
        alarm_set_info = PromAlarmSet.get_object_info_by_name(db, alarm_set_name)
        template_info = PromAlarmTemplate.get_object_info_by_uuid(db, host_info["template_uuid"])
        alarm_group_data = {
            "name": PromCommonUtils.get_alarm_group_name(host_info, template_info),
            "alarm_set_uuid": alarm_set_info["uuid"],
            "comment": f"{host_info['name']}:{host_info['ip']} 的自定义告警规则组"
        }
        result = self.prom_alarm_group_handler.add_alarm_group_to_database(db, alarm_group_data)
        if not result:
            logger.error(f"host_add_alarm_rule: create alarm group:{alarm_group_data['name']} failed")
            return False

        # 主机从主机组继承的告警模板
        host_group_info = PromHostGroup.get_object_info_by_name(db, host_info["host_group"])
        result = self.prom_alarm_template_handler.host_bind_template(db, host_info["uuid"], host_group_info["templates_uuid"])
        if not result:
            logger.error(f"host_add_alarm_rule: {host_info['name']} bind template:{host_group_info['templates_uuid']} failed")
            return False

        return True


    def add_host(self, db: Session, data: dict):
        """
        添加一台新的主机
        """
        try:
            # 创建主机自定义的告警模板
            result = self.host_add_alarm_template(db, data)
            if not result:
                logger.error(f"add alarm template failed")
                return False

            # 添加到数据库
            result = self.add_host_to_database(db, data)
            if not result:
                PromAlarmTemplate.delete_object_by_uuid(db, data['template_uuid'])
                logger.error(f"add host to database failed")
                return False
            host_info = PromHostInfo.get_object_info_by_name(db, data["name"])

            # 添加consul服务， 为了调试暂时关闭
            result = self.add_host_to_consul(db, host_info)
            if not result:
                self.delete_host(db, host_info)
                logger.error(f"add host to consul failed")
                return False

            # 添加告警规则
            result = self.host_add_alarm_rule(db, host_info)
            if not result:
                self.delete_host(db, host_info)
                logger.error(f"add host alarm rule failed")
                return False
            
            # 推送到事件审计
            self.push_to_audit_event(db, "add_host", data)
        except Exception as e:
            message = f"add host error"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return True



    def update_host_from_consul(self, db: Session, data: dict):
        """
        更新consul里面的数据
        """
        if "host_group_uuid" not in data:
            host_info = PromHostInfo.get_object_info_by_uuid(db, data["uuid"])
            data["host_group_uuid"] = host_info["host_group_uuid"]
        host_group_info = PromHostGroup.get_object_info_by_uuid(db, data["host_group_uuid"])

        # 更新标签
        data["labels"]["i_department"] = host_group_info["department"]
        data["labels"]["i_host_group"] = host_group_info["name"]
        data["labels"]["i_host_name"] = data["name"]

        if strtobool(str(data["state"])):
            result = self.consul_client.create_consul_service(data)
        else:
            result = self.consul_client.delete_consul_service(data["uuid"])

        if not result:
            logger.error(f"update host from consul failed {data['ip']}:{data['port']}")
            return False

        return True



    def update_host(self, db: Session, data: dict):
        """
        修改主机信息
        """
        try:
            host_object = PromHostInfo.get_object_by_uuid(db, data["uuid"])
            if not host_object:
                # 如果不存在， 则异常返回
                logger.error(f"update host failed, uuid:{data['uuid']} not exist")
                return False

            # 更新consul服务的主机信息
            result = self.update_host_from_consul(db, data)
            if not result:
                logger.error("update host from consul failed")
                return False

            # 更新数据库的主机信息
            result = PromHostInfo.update(db, data)
            if not result:
                logger.error(f"update host info to database failed")
                return False
        except Exception as e:
            message = f"update host error uuid:{data['uuid']}"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return True


    def delet_alarm_rule(self, db: Session, host_uuid: int):
        host_info = PromHostInfo.get_object_info_by_uuid(db, host_uuid)

        # 删除主机自定义的告警模板
        template_info = PromAlarmTemplate.get_object_info_by_uuid(db, host_info["template_uuid"])
        for strategy_info in template_info["strategies"]:
            PromAlarmStrategy.delete_object_by_uuid(db, strategy_info['uuid'])

        PromAlarmTemplate.delete_object_by_uuid(db, template_info['uuid'])

        # 查找主机相关的告警规则是否存在
        alarm_set_name = PromCommonUtils.get_alarm_set_name(host_info)
        alarm_set_object = PromAlarmSet.get_object_by_name(db, alarm_set_name)
        if not alarm_set_object:
            logger.warning(f"delet_alarm_rule nothing, {alarm_set_name} not exist")
            return True

        # 删除数据库里: 主机相关的所有告警规则
        alarm_set_info = PromAlarmSet.get_object_info_by_name(db, alarm_set_name)
        for alarm_group_info in alarm_set_info["alarm_groups"]:
            for alarm_rule_info in alarm_group_info["alarm_rules"]:
                PromAlarmRule.delete_object_by_uuid(db, alarm_rule_info["uuid"])
            PromAlarmGroup.delete_object_by_uuid(db, alarm_group_info["uuid"])

        # 删除prometheus里面的告警规则
        result = self.prom_alarm_set_handler.delete_alarm_set(db, alarm_set_info)
        if not result:
            logger.error("delete_host: delete alarm set failed")
            return False

        return True




    def delete_host(self, db: Session, data: dict):
        """
        删除主机信息
        """
        try:
            host_object = PromHostInfo.get_object_by_uuid(db, data['uuid'])
            if not host_object:
                # 如果不存在， 则异常返回
                logger.error(f"delete host failed, uuid:{data['uuid']} not exist")
                return False

            # 从consul中删除
            if not self.consul_client.delete_consul_service(data['uuid']):
                logger.error(f"delete host uuid:{data['uuid']} from consul failed")
                return False

            # 删除prometheus的告警规则
            if not self.delet_alarm_rule(db, data['uuid']):
                logger.error(f"delete host uuid:{data['uuid']} alarm rule  failed")
                return False

            # 从数据库中删除
            PromHostInfo.delete_object_by_uuid(db, data['uuid'])
            
            # 推送到事件审计
            self.push_to_audit_event(db, "delete_host", data)
        except Exception as e:
            message = f"delete host uuid:{data['uuid']} error => {e}"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return True



    def get_host_template(self, db: Session, uuid: int):
        """
        获取主机已经绑定的告警模板信息
        """
        try:
            host_object = PromHostInfo.get_object_by_uuid(db, uuid)
            if not host_object:
                # 如果不存在， 则异常返回
                logger.error(f"get host template failed, host uuid:{uuid} not exist")
                return False, None

            template_data = []
            host_info = PromHostInfo.get_object_info_by_uuid(db, uuid)

            # 获取主机自定义的模板信息
            host_template_info = PromAlarmTemplate.get_object_info_by_uuid(db, host_info["template_uuid"])
            template_data.append(host_template_info)

            # 获取主机继承的模板信息
            host_group_info = PromHostGroup.get_object_info_by_name(db, host_info["host_group"])
            for template_uuid in host_group_info["templates_uuid"]:
                template_info = PromAlarmTemplate.get_object_info_by_uuid(db, template_uuid)
                template_data.append(template_info)
        except Exception as e:
            message = f"get host:{uuid} template error => {e}"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return True, template_data



    def put_host_template(self, db: Session, data: dict):
        """
        更新主机自定义的告警模板
        """
        try:
            # 检查host是否存在
            host_object = PromHostInfo.get_object_by_uuid(db, data['uuid'])
            if not host_object:
                # 如果不存在， 则异常返回
                logger.error(f"put host template failed, uuid:{data['uuid']} not exist")
                return False

            host_info = PromHostInfo.get_object_info_by_uuid(db, data['uuid'])
            host_group_info = PromHostGroup.get_object_info_by_uuid(db, host_info['host_group_uuid'])
            templates_uuid = host_group_info["templates_uuid"]
            templates_uuid.append(host_info["template_uuid"])

            # 更新绑定的模板
            result = self.prom_alarm_template_handler.host_update_bind_template(db, host_info["uuid"], templates_uuid)
            if not result:
                logger.error(f"put_host_template: {host_info['uuid']} update bind {templates_uuid} failed")
                return False
        except Exception as e:
            message = f"put host:{data['uuid']} template error => {e}"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return True


    def get_host_strategy(self, db: Session, uuid: int):
        """
        获取主机所有的告警策略
        """
        try:
            strategy_data = []
            host_object = PromHostInfo.get_object_by_uuid(db, uuid)
            if not host_object:
                # 如果不存在， 则异常返回
                logger.error(f"get host strategy failed, host uuid:{uuid} not exist")
                return False, strategy_data

            host_info = PromHostInfo.get_object_info_by_uuid(db, uuid)

            # 获取主机自定义的模板信息
            host_template_info = PromAlarmTemplate.get_object_info_by_uuid(db, host_info["template_uuid"])
            strategy_data.extend(host_template_info["strategies"])

            # 获取主机继承的模板信息
            host_group_info = PromHostGroup.get_object_info_by_name(db, host_info["host_group"])
            for template_uuid in host_group_info["templates_uuid"]:
                template_info = PromAlarmTemplate.get_object_info_by_uuid(db, template_uuid)
                strategy_data.extend(template_info["strategies"])
        except Exception as e:
            message = f"get host:{uuid} strategy error => {e}"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return True, strategy_data



    def post_host_strategy(self, db: Session, data: dict):
        """
        创建主机自定义告警策略
        """
        try:
            host_uuid = data.pop("host_uuid")
            # 检查host是否存在
            host_object = PromHostInfo.get_object_by_uuid(db, host_uuid)
            if not host_object:
                # 如果不存在， 则异常返回
                logger.error(f"post host strategy failed, host_uuid:{host_uuid} not exist")
                return False

            alarm_template_object = PromAlarmTemplate.get_object_by_uuid(db, host_object.template_uuid)
            if not alarm_template_object:
                # 如果不存在， 则异常返回
                logger.error(f"post host strategy failed, host_object.template_uuid:{host_object.template_uuid} not exist")
                return False

            # 添加告警策略
            data["template_uuid"] = host_object.template_uuid
            data["classes"] = alarm_template_object.classes
            result = self.prom_alarm_strategy_handler.add_alarm_strategy(db, data)
            if not result:
                logger.error(f"post_host_strategy: add alarm strategy:{data['name']} failed")
                return False
        except Exception as e:
            message = f"post_host_strategy: add alarm strategy:{data['name']} error => {e}"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return True


    def put_host_strategy(self, db: Session, data: dict):
        """
        更新主机自定义告警策略
        """
        try:
            host_uuid = data.pop("host_uuid")
            # 检查host是否存在
            host_object = PromHostInfo.get_object_by_uuid(db, host_uuid)
            if not host_object:
                # 如果不存在， 则异常返回
                logger.error(f"put host strategy failed, host_uuid:{host_uuid} not exist")
                return False

            alarm_template_object = PromAlarmTemplate.get_object_by_uuid(db, host_object.template_uuid)
            if not alarm_template_object:
                # 如果不存在， 则异常返回
                logger.error(
                    f"post host strategy failed, host_object.template_uuid:{host_object.template_uuid} not exist")
                return False

            # 更新告警策略
            data["template_uuid"] = host_object.template_uuid
            data["classes"] = alarm_template_object.classes
            result = self.prom_alarm_strategy_handler.update_alarm_strategy(db, data)
            if not result:
                logger.error(f"put_host_strategy: add alarm strategy:{data['name']} failed")
                return False
        except Exception as e:
            message = f"put_host_strategy: update alarm strategy:{data['name']} error => {e}"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return True
    
    
    def push_to_audit_event(self, db: Session, event_name: str, data: dict):
        audit_event_handler = AuditEventHandler()
        event_data = {
            "event_name": event_name,
            "event_type": "monitor_metrics",
            "is_notice": True,
            "comment": str(data)
        }
        audit_event_handler.add_audit_event(db, event_data)
        return True
